home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir24 / jnos110g.zip / TELNET.C < prev    next >
C/C++ Source or Header  |  1994-04-17  |  15KB  |  574 lines

  1. /* Internet Telnet client
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. /* Mods by PA0GRI */
  5. #ifdef  __TURBOC__
  6. #include <io.h>
  7. #include <fcntl.h>
  8. #endif
  9. #include "global.h"
  10. #include "mbuf.h"
  11. #include "socket.h"
  12. #include "telnet.h"
  13. #include "session.h"
  14. #include "proc.h"
  15. #include "tty.h"
  16. #include "commands.h"
  17. #include "netuser.h"
  18.   
  19. static int filemode __ARGS((FILE *fp,int mode));
  20. #define CTLZ    26
  21.   
  22. int Refuse_echo = 0;
  23. int Tn_cr_mode = 0;    /* if true turn <cr> to <cr-nul> */
  24.   
  25. #undef DEBUG 1
  26.   
  27. #ifdef  DEBUG
  28. char *T_options[] = {
  29.     "Transmit Binary",      /* 0 */
  30.     "Echo",             /* 1 */
  31.     "",             /* 2 */
  32.     "Suppress Go Ahead",        /* 3 */
  33.     "",             /* 4 */
  34.     "Status",           /* 5 */
  35.     "Timing Mark"           /* 6 */
  36. #ifdef  TN_KK6JQ
  37.     ,
  38.     "Remote Controlled Transmission/Echo",
  39.     "Negotiate Line Width",
  40.     "Negotiate Page Size",
  41.     "Negotiate CR Disposition",
  42.     "Negotiate Horz Tab Stops",
  43.     "Negotiate Horz tab Disposition",
  44.     "Negotiate FormFeed Disposition",
  45.     "Negotiate Vert Tab Stops",
  46.     "Negotiate Vert Tab Disposition",
  47.     "Negotiate LineFeed Disposition",
  48.     "Extended ASCII",
  49.     "Force Logout",
  50.     "Byte Macro",
  51.     "Data Entry Terminal",
  52.     "Protocol supdup",
  53.     "supdup",
  54.     "Send Location",
  55.     "Terminal Type",
  56.     "End of Record"
  57. #endif  /* TN_KK6JQ */
  58. };
  59. #endif  /* DEBUG */
  60.   
  61. #if defined BBSSESSION || defined TELNETSESSION || defined TTYLINKSESSION
  62. /* Execute user telnet,ttylink or bbs command */
  63. int
  64. dotelnet(argc,argv,p)
  65. int argc;
  66. char *argv[];
  67. void *p;
  68. {
  69.     struct session *sp;
  70.     struct sockaddr_in fsocket;
  71.     char *name;
  72.     int split = 0;
  73.     int bbs = 0;
  74.   
  75.     /*Make sure this comes from console - WG7J*/
  76.     if(Curproc->input != Command->input)
  77.         return 0;
  78.   
  79.     name = argv[1];     /* This is not valid for 'bbs' command ! */
  80. #ifdef BBSSESSION
  81.     if(*argv[0] == 'b') {
  82.         name = "Local BBS";
  83.         bbs = 1;
  84.     }
  85. #ifdef TTYLINKSESSION
  86.     else
  87. #endif
  88. #endif
  89. #ifdef TTYLINKSESSION
  90.         if((strlen(argv[0]) > 1) && (argv[0][1] == 't')) /* tty-link command */
  91.             split = 1;
  92. #endif
  93.   
  94.     /* Allocate a session descriptor */
  95.     if((sp = newsession(name,TELNET,split)) == NULLSESSION){
  96.         tputs(TooManySessions);
  97.         return 1;
  98.     }
  99.     fsocket.sin_family = AF_INET;
  100.     if(argc < 3) {
  101.         if(split)
  102.             fsocket.sin_port = IPPORT_TTYLINK;
  103.         else
  104.             fsocket.sin_port = IPPORT_TELNET;
  105.     } else {
  106.         fsocket.sin_port = atoip(argv[2]);
  107.     }
  108. #ifdef BBSSESSION
  109.     if(bbs) {
  110.         /* 127.0.0.1 is the loopback interface */
  111.         fsocket.sin_addr.s_addr = 0x7f000001L;
  112.     } else
  113. #endif
  114.     {
  115.         tprintf("Resolving %s... ",sp->name);
  116.         if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  117.             tprintf(Badhost,sp->name);
  118.             keywait(NULLCHAR,1);
  119.             freesession(sp);
  120.             return 1;
  121.         }
  122.     }
  123.     if((sp->s = socket(AF_INET,SOCK_STREAM,0)) == -1){
  124.         tputs(Nosock);
  125.         keywait(NULLCHAR,1);
  126.         freesession(sp);
  127.         return 1;
  128.     }
  129.     return tel_connect(sp,(char *)&fsocket,SOCKSIZE);
  130. }
  131. #endif /* TELNET || TTYLINK || BBS */
  132.   
  133. /* Generic interactive connect routine, used by Telnet, AX.25, NET/ROM */
  134. int
  135. tel_connect(sp,fsocket,len)
  136. struct session *sp;
  137. char *fsocket;
  138. int len;
  139. {
  140.     unsigned int index;
  141.     struct telnet tn;
  142.   
  143.     index = (unsigned int) (sp - Sessions);
  144.     memset((char *)&tn,0,sizeof(tn));
  145.     tn.eolmode = Tn_cr_mode;
  146.     tn.session = sp;    /* Upward pointer */
  147.     sp->cb.telnet = &tn;    /* Downward pointer */
  148.     sockmode(sp->s,SOCK_ASCII); /* Default to ascii mode */
  149.   
  150.     tprintf("Trying %s...\n",psocket((struct sockaddr *)fsocket));
  151.     if(connect(sp->s,fsocket,len) == -1){
  152.         tprintf("%s session %u failed: %s errno %d\n",
  153.         Sestypes[sp->type], index, sockerr(sp->s),errno);
  154.   
  155.         keywait(NULLCHAR,1);
  156.         freesession(sp);
  157.         return 1;
  158.     }
  159.     tprintf("%s session %u connected to %s\n",
  160.     Sestypes[sp->type],index,sp->name);
  161.     tnrecv(&tn);
  162.     return 0;
  163. }
  164.   
  165. /* Telnet input routine, common to both telnet and ttylink */
  166. void
  167. tnrecv(tn)
  168. struct telnet *tn;
  169. {
  170.     int c,s,index;
  171.     struct session *sp;
  172.     char *cp;
  173.   
  174.     sp = tn->session;
  175.     s = sp->s;
  176.   
  177.     index = (unsigned int) (sp - Sessions);
  178.   
  179.     /* Fork off the transmit process */
  180.     sp->proc1 = newproc("tel_out",1024,tel_output,0,tn,NULL,0);
  181.   
  182.     /* Process input on the connection */
  183.     while((c = recvchar(s)) != -1){
  184.         if(c != IAC){
  185. #ifdef notdef
  186.             /* Allow international character sets to pass - WG7J */
  187.             /* Ordinary character */
  188.             if(!tn->remote[TN_TRANSMIT_BINARY])
  189.                 c &= 0x7f;
  190. #endif
  191.             tputc((char)c);
  192.             continue;
  193.         }
  194.         /* IAC received, get command sequence */
  195.         c = recvchar(s);
  196.         switch(c){
  197.             case WILL:
  198.                 c = recvchar(s);
  199.                 willopt(tn,c);
  200.                 break;
  201.             case WONT:
  202.                 c = recvchar(s);
  203.                 wontopt(tn,c);
  204.                 break;
  205.             case DO:
  206.                     c = recvchar(s);
  207.                     doopt(tn,c);
  208.                     break;
  209.             case DONT:
  210.                 c = recvchar(s);
  211.                 dontopt(tn,c);
  212.                 break;
  213. #ifdef  TN_KK6JQ
  214.             case SB:
  215.                 c = recvchar(s);
  216.                 dosb(tn,c);
  217.                 break;
  218. #endif  /* TN_KK6JQ */
  219.             case IAC:   /* Escaped IAC */
  220.                 tputc(IAC);
  221.                 break;
  222.         }
  223.     }
  224.     /* A close was received from the remote host.
  225.      * Notify the user, kill the output task and wait for a response
  226.      * from the user before freeing the session.
  227.      */
  228.     sockmode(sp->output,SOCK_ASCII); /* Restore newline translation */
  229.     cp = sockerr(s);
  230.     tprintf("%s session %u", Sestypes[sp->type],index);
  231.     tprintf(" closed: %s\n", cp != NULLCHAR ? cp : "EOF");
  232.     killproc(sp->proc1);
  233.     sp->proc1 = NULLPROC;
  234.     close_s(sp->s);
  235.     sp->s = -1;
  236.     keywait(NULLCHAR,1);
  237.     freesession(sp);
  238. }
  239.   
  240. /* User telnet output task, started by user telnet command */
  241. void
  242. tel_output(unused,tn1,p)
  243. int unused;
  244. void *tn1;
  245. void *p;
  246. {
  247.     struct session *sp;
  248.     int c;
  249.     struct telnet *tn;
  250.   
  251.     tn = (struct telnet *)tn1;
  252.     sp = tn->session;
  253.   
  254.     /* Send whatever's typed on the terminal */
  255.     while((c = recvchar(sp->input)) != EOF){
  256.         usputc(sp->s,(char)c);
  257.         if(!tn->remote[TN_ECHO] && sp->record != NULLFILE)
  258.             putc(c,sp->record);
  259.   
  260.         /* By default, output is transparent in remote echo mode.
  261.          * If eolmode is set, turn a cr into cr-null.
  262.          * This can only happen when in remote echo (raw) mode, since
  263.          * the tty driver normally maps \r to \n in cooked mode.
  264.          */
  265.         if(c == '\r' && tn->eolmode)
  266.             usputc(sp->s,'\0');
  267.   
  268.         if(tn->remote[TN_ECHO])
  269.             usflush(sp->s);
  270.     }
  271.     /* Make sure our parent doesn't try to kill us after we exit */
  272.     sp->proc1 = NULLPROC;
  273. }
  274. int
  275. doecho(argc,argv,p)
  276. int argc;
  277. char *argv[];
  278. void *p;
  279. {
  280.     if(argc < 2){
  281.         if(Refuse_echo)
  282.             tputs("Refuse\n");
  283.         else
  284.             tputs("Accept\n");
  285.     } else {
  286.         if(argv[1][0] == 'r')
  287.             Refuse_echo = 1;
  288.         else if(argv[1][0] == 'a')
  289.             Refuse_echo = 0;
  290.         else
  291.             return -1;
  292.     }
  293.     return 0;
  294. }
  295. /* set for unix end of line for remote echo mode telnet */
  296. int
  297. doeol(argc,argv,p)
  298. int argc;
  299. char *argv[];
  300. void *p;
  301. {
  302.     if(argc < 2){
  303.         if(Tn_cr_mode)
  304.             tputs("null\n");
  305.         else
  306.             tputs("standard\n");
  307.     } else {
  308.         if(argv[1][0] == 'n')
  309.             Tn_cr_mode = 1;
  310.         else if(argv[1][0] == 's')
  311.             Tn_cr_mode = 0;
  312.         else {
  313.             tprintf("Usage: %s [standard|null]\n",argv[0]);
  314.             return -1;
  315.         }
  316.     }
  317.     return 0;
  318. }
  319.   
  320. /* The guts of the actual Telnet protocol: negotiating options */
  321. void
  322. willopt(tn,opt)
  323. struct telnet *tn;
  324. int opt;
  325. {
  326.     int ack;
  327.   
  328. #ifdef  DEBUG
  329.     printf("recv: will ");
  330.     if(uchar(opt) <= NOPTIONS)
  331.         printf("%s\n",T_options[opt]);
  332.     else
  333.         printf("%u\n",opt);
  334. #endif
  335.   
  336.     switch(uchar(opt)){
  337.         case TN_TRANSMIT_BINARY:
  338.         case TN_ECHO:
  339.         case TN_SUPPRESS_GA:
  340.             if(tn->remote[uchar(opt)] == 1)
  341.                 return;     /* Already set, ignore to prevent loop */
  342.             if(uchar(opt) == TN_ECHO){
  343.                 if(Refuse_echo){
  344.                 /* User doesn't want to accept */
  345.                     ack = DONT;
  346.                     break;
  347.                 } else {
  348.                 /* Put tty into raw mode */
  349.                     tn->session->ttystate.edit = 0;
  350.                     tn->session->ttystate.echo = 0;
  351.                     sockmode(tn->session->s,SOCK_BINARY);
  352.                     sockmode(tn->session->input,SOCK_BINARY);
  353.                     sockmode(tn->session->output,SOCK_BINARY);
  354.                     if(tn->session->record != NULLFILE)
  355.                         filemode(tn->session->record,SOCK_BINARY);
  356.   
  357.                 }
  358.             }
  359.             tn->remote[uchar(opt)] = 1;
  360.             ack = DO;
  361.                 break;
  362. #ifdef TN_KK6JQ
  363.         case TN_TERM_TYPE:  /* We are the client, we don't want this */
  364. #endif /* TN_KK6JQ */
  365.         default:
  366.             ack = DONT; /* We don't know what he's offering; refuse */
  367.     }
  368.     answer(tn,ack,opt);
  369. }
  370. void
  371. wontopt(tn,opt)
  372. struct telnet *tn;
  373. int opt;
  374. {
  375. #ifdef  DEBUG
  376.     printf("recv: wont ");
  377.     if(uchar(opt) <= NOPTIONS)
  378.         printf("%s\n",T_options[uchar(opt)]);
  379.     else
  380.         printf("%u\n",uchar(opt));
  381. #endif
  382.     if(uchar(opt) <= NOPTIONS){
  383.         if(tn->remote[uchar(opt)] == 0)
  384.             return;     /* Already clear, ignore to prevent loop */
  385.             tn->remote[uchar(opt)] = 0;
  386.             if(uchar(opt) == TN_ECHO){
  387.             /* Put tty into cooked mode */
  388.                 tn->session->ttystate.edit = 1;
  389.                 tn->session->ttystate.echo = 1;
  390.                 sockmode(tn->session->s,SOCK_ASCII);
  391.                 sockmode(tn->session->input,SOCK_ASCII);
  392.                 sockmode(tn->session->output,SOCK_ASCII);
  393.                 if(tn->session->record != NULLFILE)
  394.                     filemode(tn->session->record,SOCK_ASCII);
  395.             }
  396.     }
  397.     answer(tn,DONT,opt);    /* Must always accept */
  398. }
  399.   
  400. #ifdef  TN_KK6JQ
  401. void
  402. dosb(tn, opt)
  403. struct telnet *tn;
  404. int opt;
  405. {
  406.     struct session *sp;
  407.     int s, c;
  408.     char    *name, resp_buf[64];
  409.     int resp_len = 0;
  410.   
  411.     sp = tn->session;
  412.     s = sp->s;
  413.   
  414.     switch (uchar(opt)) {
  415.         case TN_TERM_TYPE:
  416.         /* make sure qualifier is SEND */
  417.         /* ignore otherwise */
  418.   
  419.         /* Karn code is cavalier about EOF when in an IAC sequence.. */
  420.             c = recvchar(s);
  421.             if (c == TS_SEND) {
  422.                 c = recvchar(s);    /* should be IAC */
  423.                 c = recvchar(s);    /* should be SE */
  424. /*
  425.  * Should check to make sure sequence includes IAC, SE and somehow fail
  426.  * if it doesn't.
  427.  */
  428.                 name = getenv("TERM");
  429.                 if ((name==NULL) || ((resp_len = strlen(name)) > 58))
  430.                     name = "UNKNOWN";
  431.                     sprintf(resp_buf, "%c%c%c%c%s%c%c",
  432.                     IAC, SB, TN_TERM_TYPE, TS_IS, name,
  433.                     IAC, SE);
  434. /*
  435. original code:
  436.             resp_len += 6;
  437.   
  438.    Oops! What is the REAL length of this string? It's not just the control
  439.    characters... the length of the terminal name needs to be added in
  440. */
  441.                     resp_len += strlen(resp_buf);
  442.             }
  443.             break;
  444.         default:
  445.             break;
  446.     }
  447.   
  448.     if (resp_len > 0)
  449.         send(s, resp_buf, resp_len, 0);
  450. }
  451. #endif  /* TN_KK6JQ */
  452.   
  453. void
  454. doopt(tn,opt)
  455. struct telnet *tn;
  456. int opt;
  457. {
  458.     int ack;
  459.   
  460. #ifdef  DEBUG
  461.     printf("recv: do ");
  462.     if(uchar(opt) <= NOPTIONS)
  463.         printf("%s\n",T_options[uchar(opt)]);
  464.     else
  465.         printf("%u\n",uchar(opt));
  466. #endif
  467.     switch(uchar(opt)){
  468.         case TN_SUPPRESS_GA:
  469. #ifdef  TN_KK6JQ
  470.         case TN_TERM_TYPE:
  471. #endif  /* TN_KK6JQ */
  472.             if(tn->local[uchar(opt)] == 1)
  473.                 return;     /* Already set, ignore to prevent loop */
  474.                 tn->local[uchar(opt)] = 1;
  475.                 ack = WILL;
  476.                 break;
  477.         default:
  478.             ack = WONT; /* Don't know what it is */
  479.     }
  480.     answer(tn,ack,opt);
  481. }
  482. void
  483. dontopt(tn,opt)
  484. struct telnet *tn;
  485. int opt;
  486. {
  487. #ifdef  DEBUG
  488.     printf("recv: dont ");
  489.     if(uchar(opt) <= NOPTIONS)
  490.         printf("%s\n",T_options[uchar(opt)]);
  491.     else
  492.         printf("%u\n",uchar(opt));
  493. #endif
  494.     if(uchar(opt) <= NOPTIONS){
  495.         if(tn->local[uchar(opt)] == 0){
  496.             /* Already clear, ignore to prevent loop */
  497.             return;
  498.         }
  499.         tn->local[uchar(opt)] = 0;
  500.     }
  501.     answer(tn,WONT,opt);
  502. }
  503. void
  504. answer(tn,r1,r2)
  505. struct telnet *tn;
  506. int r1,r2;
  507. {
  508.     char s[3];
  509.   
  510. #ifdef  DEBUG
  511.     switch(r1){
  512.         case WILL:
  513.             printf("sent: will ");
  514.             break;
  515.         case WONT:
  516.             printf("sent: wont ");
  517.             break;
  518.         case DO:
  519.             printf("sent: do ");
  520.             break;
  521.         case DONT:
  522.             printf("sent: dont ");
  523.             break;
  524.     }
  525.     if(r2 <= NOPTIONS)
  526.         printf("%s\n",T_options[r2]);
  527.     else
  528.         printf("%u\n",r2);
  529. #endif
  530.   
  531.     s[0] = IAC;
  532.     s[1] = r1;
  533.     s[2] = r2;
  534.     send(tn->session->s,s,3,0);
  535. }
  536. #ifdef  __TURBOC__
  537. /* Set end-of-line translation mode on file */
  538. static int
  539. filemode(fp,mode)
  540. FILE *fp;
  541. int mode;
  542. {
  543.     int omode;
  544.   
  545.     if(fp == NULLFILE)
  546.         return -1;
  547.   
  548.     if(fp->flags & _F_BIN)
  549.         omode = SOCK_BINARY;
  550.     else
  551.         omode = SOCK_ASCII;
  552.   
  553.     switch(mode){
  554.         case SOCK_BINARY:
  555.             fp->flags = _F_BIN;
  556.             setmode(fileno(fp),O_BINARY);
  557.             break;
  558.         case SOCK_ASCII:
  559.             fp->flags &= ~_F_BIN;
  560.             setmode(fileno(fp),O_TEXT);
  561.             break;
  562.     }
  563.     return omode;
  564. }
  565. #else
  566. static int
  567. filemode(fp,mode)
  568. FILE *fp;
  569. int mode;
  570. {
  571.     return 0;
  572. }
  573. #endif
  574.